/*
* Copyright (c) 1993-2003, Silicon Graphics, Inc.
* All Rights Reserved
*
* Permission to use, copy, modify, and distribute this software for any
* purpose and without fee is hereby granted, provided that the above
* copyright notice appear in all copies and that both the copyright
* notice and this permission notice appear in supporting documentation,
* and that the name of Silicon Graphics, Inc. not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission.
*
* THE MATERIAL EMBODIED ON THIS SOFTWARE IS PROVIDED TO YOU "AS-IS" AND
* WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
* FITNESS FOR A PARTICULAR PURPOSE.  IN NO EVENT SHALL SILICON
* GRAPHICS, INC.  BE LIABLE TO YOU OR ANYONE ELSE FOR ANY DIRECT,
* SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
* OR ANY DAMAGES WHATSOEVER, INCLUDING WITHOUT LIMITATION, LOSS OF
* PROFIT, LOSS OF USE, SAVINGS OR REVENUE, OR THE CLAIMS OF THIRD
* PARTIES, WHETHER OR NOT SILICON GRAPHICS, INC.  HAS BEEN ADVISED OF
* THE POSSIBILITY OF SUCH LOSS, HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE POSSESSION, USE
* OR PERFORMANCE OF THIS SOFTWARE.
*
* US Government Users Restricted Rights 
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in FAR 52.227.19(c)(2) or subparagraph
* (c)(1)(ii) of the Rights in Technical Data and Computer Software
* clause at DFARS 252.227-7013 and/or in similar or successor clauses
* in the FAR or the DOD or NASA FAR Supplement.  Unpublished - rights
* reserved under the copyright laws of the United States.
*
* Contractor/manufacturer is:
*	Silicon Graphics, Inc.
*	1500 Crittenden Lane
*	Mountain View, CA  94043
*	United State of America
*
* OpenGL(R) is a registered trademark of Silicon Graphics, Inc.
*/

/*
*  cube.c
*  This program demonstrates a single modeling transformation,
*  glScalef() and a single viewing transformation, gluLookAt().
*  A wireframe cube is rendered.
*/
#include <stdlib.h>
//#include <glut.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include "../glut/glaux.h"
#include "../glut/SOIL.h"
#include "../glut/glut.h"

GLfloat rotate;
GLfloat rotateX;
GLfloat rotateY;
GLfloat rotateZ;
GLuint texture[1];    // Storage For One Texture

struct vertex
{
	float v[3];
};

struct faces
{
	int f[3];
};

struct normals
{
	float n[3];
};

typedef struct vertex Vertex;
typedef struct faces Faces;
typedef struct vertex VertexNormals;
typedef struct normals Normals;

Vertex vertex[10000]; // 2-Dimensionales Array
Faces faces[10000];
Normals normals[10000];
VertexNormals vertexnormals[10000];


/* Ich hatte versucht, die Objekte zu texturieren, nur leider gab es immer wieder Probleme beim Einbinden der glaux.lib,
obwohl ich diese in den Linker/Eingabe/Zusätzliche Abhängigkeiten eingefügt habe. Dann habe ich es mit soil.lib probiert,
was jedoch leider auch die selben Fehler ausgegeben hatte. Deswegen ist der ganze Textur-Code nun auskommentiert, damit
überhaupt nocht etwas funktioniert ;)*/
/*Mit den Tasten W A S D kann man die Objekte bewegen*/

/* //mit SOIL
int LoadGLTextures()                                    // Load Bitmaps And Convert To Textures
{
    //oad an image file directly as a new OpenGL texture
    texture[0] = SOIL_load_OGL_texture
        (
        "schach.bmp",
        SOIL_LOAD_AUTO,
        SOIL_CREATE_NEW_ID,	
        SOIL_FLAG_INVERT_Y
        );
 
    if(texture[0] == 0)
        return false;
 
 
    // Typical Texture Generation Using Data From The Bitmap
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
 
    return true;                                        // Return Success
}*/

//mit glaux

AUX_RGBImageRec *LoadBMP(char *Filename)                    // Loads A Bitmap Image
{
    FILE *File=NULL;                            // File Handle
if (!Filename)                              // Make Sure A Filename Was Given
{
    return NULL;                            // If Not Return NULL
}
File=fopen(Filename,"r");                       // Check To See If The File Exists
if (File)                               // Does The File Exist?
{
    fclose(File);                           // Close The Handle
    return auxDIBImageLoad(Filename);               // Load The Bitmap And Return A Pointer
}
    return NULL;                                // If Load Failed Return NULL
}

int LoadGLTextures()                            // Load Bitmaps And Convert To Textures
{
	int Status=FALSE;                           // Status Indicator
	AUX_RGBImageRec *TextureImage[1];           // Create Storage Space For The Texture
	memset(TextureImage,0,sizeof(void *)*1);    // Set The Pointer To NULL

	// Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
	if (TextureImage[0]=LoadBMP("schach.bmp"))
	{
		Status=TRUE;                            // Set The Status To TRUE

		glGenTextures(1, &texture[0]);                  // Create The Texture	
		// Typical Texture Generation Using Data From The Bitmap
		glBindTexture(GL_TEXTURE_2D, texture[0]);

		// Generate The Texture
		glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, 
					 GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
	}

	if (TextureImage[0])                            // If Texture Exists
	{
		if (TextureImage[0]->data)                   // If Texture Image Exists
		{
			free(TextureImage[0]->data);             // Free The Texture Image Memory
		}
		free(TextureImage[0]);                      // Free The Image Structure
	}
	return Status;                              // Return The Status
}


void init(void) 
{
	/*
	if (!LoadGLTextures())                          // Jump To Texture Loading Routine ( NEW )
    {
        return FALSE;                           // If Texture Didn't Load Return FALSE ( NEW )
    }
 */
    glEnable(GL_TEXTURE_2D);                        // Enable Texture Mapping ( NEW )


	glClearColor (0.0, 0.0, 0.0, 0.0);
	glShadeModel (GL_SMOOTH);

	glEnable( GL_LIGHTING );


	//Punktlicht
	GLfloat ambient[] = { 0.0, 0.0, 0.0, 1.0 }; 
	GLfloat diffuse[] = { 0.0, 0.0, 1.0, 1.0 }; 
	GLfloat specular[] = { 1.0, 1.0, 1.0, 1.0 }; 
	GLfloat position[] = { 0.0, 10.0, 0.0, 1.0 };

	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); 
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); 
	glLightfv(GL_LIGHT0, GL_SPECULAR, specular); 
	glLightfv(GL_LIGHT0, GL_POSITION, position);

	glEnable(GL_LIGHT0);



	//gerichtetes Licht
	GLfloat ambient1[] = { 0.0, 0.0, 0.0, 1.0 }; 
	GLfloat diffuse1[] = { 1.0, 0.0, 0.0, 1.0 }; 
	GLfloat specular1[] = { 1.0, 1.0, 1.0, 1.0 }; 
	GLfloat position1[] = { 10.0, 0.0, 0.0, 0.0 };

	glLightfv(GL_LIGHT1, GL_AMBIENT, ambient1); 
	glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse1); 
	glLightfv(GL_LIGHT1, GL_SPECULAR, specular1); 
	glLightfv(GL_LIGHT1, GL_POSITION, position1);

	glEnable(GL_LIGHT1);



	//SpotLicht
	GLfloat ambient2[] = { 0.0, 0.0, 0.0, 1.0 }; 
	GLfloat diffuse2[] = { 0.0, 1.0, 0.0, 1.0 }; 
	GLfloat specular2[] = { 0.0, 1.0, 0.0, 1.0 }; 
	GLfloat position2[] = { 0.0, -5.0, 0.0, 1.0 };

	glLightfv(GL_LIGHT2, GL_AMBIENT, ambient2); 
	glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse2); 
	glLightfv(GL_LIGHT2, GL_SPECULAR, specular2); 
	glLightfv(GL_LIGHT2, GL_POSITION, position2);

	float LightDir[3] = {0.0f, 1.0f, 0.0f}; 
	glLighti(GL_LIGHT2, GL_SPOT_CUTOFF, 30); 
	glLightfv(GL_LIGHT2, GL_POSITION, position2); 
	glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, LightDir);

	glEnable(GL_LIGHT2);

	/*
	GLfloat mat_emission[] = {0.0, 0.0, 0.0, 0.0}; 
	GLfloat mat_ambient[] = { 0.25, 0.20, 0.07, 1.0 }; 
	GLfloat mat_diffuse[] = { 0.75, 0.61, 0.23, 1.0 }; 
	GLfloat mat_specular[] = { 0.63, 0.56, 0.37, 1.0 }; 
	GLfloat shininess[] = { 51.0 };
	glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); 
	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); 
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); 
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); 
	glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
	*/
}

void SchwarzStefanieDisplay(void)
{
	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glColor3f (1.0, 1.0, 1.0);
	glLoadIdentity ();             /* clear the matrix */
	// viewing transformation  
	// Look from 0.0, 0.0, 5.0 to 0.0, 0.0, 0.0 where up is 0.0, 1.0, 0.0
	gluLookAt (5.0, 5.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

	
	// Bitte ab hier die NeHe-Tutorials einarbeiten
	glRotatef (rotateX, 0.0, 1.0, 0.0);
	glRotatef (rotateY, 1.0, 0.0, 0.0);
	glRotatef (rotateZ, 0.0, 0.0, 1.0);
	glPushMatrix();

	/*
	// WÜrfel 1 (Stamm)
	glColor3f (1.0, 1.0, 1.0);
	glLoadIdentity ();
	glPopMatrix();
	glPushMatrix();
	glTranslatef (0.0, -1.0, 0.0); 
	glScalef (1.0, 2.0, 1.0); 
	glutSolidCube (1.0);		

	//Würfel 2 (Ast 1)
	glColor3f (1.0, 1.0, 1.0);
	glLoadIdentity ();
	glPopMatrix();
	glPushMatrix();
	glRotatef (60, 0.0, 0.0, 1.0);
	glTranslatef (0.0, 1.0, 0.0); 
	glScalef (1.0, 2.0, 1.0); 
	glutSolidCube (1.0);		 

	//Würfel 3 (Ast 2)
	glColor3f (1.0, 1.0, 1.0);
	glLoadIdentity ();
	glPopMatrix();
	glPushMatrix();
	glRotatef (120, 0.0, 1.0, 0.0);
	glRotatef (60, 0.0, 0.0, 1.0);
	glTranslatef (0.0, 1.0, 0.0); 
	glScalef (1.0, 2.0, 1.0); 
	glutSolidCube (1.0);		

	//Würfel 4 (Ast 3)
	glColor3f (1.0, 1.0, 1.0);
	glLoadIdentity ();
	glPopMatrix();
	glRotatef (-120, 0.0, 1.0, 0.0);
	glRotatef (60, 0.0, 0.0, 1.0);
	glTranslatef (0.0, 1.0, 0.0); 
	glScalef (1.0, 2.0, 1.0); 
	glutSolidCube (1.0);		 
	*/

	glBegin(GL_TRIANGLES);
	int temp;
	for(int x=0; x<10000; x++) //Zeilen
	{
		for (int y=0; y<3; y++) //Spalten
		{
			temp = faces[x].f[y];

			//glNormal3f(normals[x].n[0], normals[x].n[1], normals[x].n[2]);
			glNormal3f(vertexnormals[faces[x].f[y]-1].v[0], vertexnormals[faces[x].f[y]-1].v[1], vertexnormals[faces[x].f[y]-1].v[2]);
			
			glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);  // Bottom Left Of The Texture and Quad
			glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);  // Bottom Right Of The Texture and Quad
			glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);  // Top Right Of The Texture and Quad
			glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);  // Top Left Of The Texture and Quad

			glVertex3f(vertex[temp-1].v[0], vertex[temp-1].v[1], vertex[temp-1].v[2]);

		}
	}
	glEnd();
	
	glFlush ();
}

void reshape (int w, int h)
{
	glViewport (0, 0, (GLsizei) w, (GLsizei) h); 
	glMatrixMode (GL_PROJECTION);
	glLoadIdentity ();
	glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0);
	glMatrixMode (GL_MODELVIEW);
}

void keyboard(unsigned char key, int x, int y)
{
	switch (key) {
	case 27:
		exit(0);
		break;
	case 'a':
		rotateX +=5.0f;
		break;
	case 'd':
		rotateX -=5.0f;
		break;
	case 'w':
		rotateY +=5.0f;
		break;
	case 's':
		rotateY -=5.0f;
		break;
	case 'r':
		rotateZ -=5.0f;
		break;
	case 'f':
		rotateY +=5.0f;
		break;
	}
}

void idle ( void)
{
	rotate+= 0.2 ;
	glutPostRedisplay();
}

void calculateNormal(Vertex vertex[], int j)
{
	float v11, v12, v13; //Vektor1
	float v21, v22, v23; //Vektor2
	float k1, k2, k3; //Kreuzprodukt
	int temp1, temp2, temp3;

	for(int x=0; x<j; x++) //Zeilen
	{
			temp1 = faces[x].f[0]-1;
			temp2 = faces[x].f[1]-1;
			temp3 = faces[x].f[2]-1;
		
			v11 = (vertex[temp1].v[0] - vertex[temp3].v[0]); // v[0] -> Bezugspunkt ... v1 = v[1]-v[0]
			v12 = (vertex[temp1].v[1] - vertex[temp3].v[1]);
			v13 = (vertex[temp1].v[2] - vertex[temp3].v[2]);

			v21 = (vertex[temp2].v[0] - vertex[temp3].v[0]); // v[0] -> Bezugspunkt ... v2 = v[2]-v[0]
			v22 = (vertex[temp2].v[1] - vertex[temp3].v[1]);
			v23 = (vertex[temp2].v[2] - vertex[temp3].v[2]); 

			k1 = v12 * v23 - v13 * v22; // Kreuzprodukt v1 x v2
			k2 = v13 * v21 - v11 * v23;
			k3 = v11 * v22 - v12 * v21;

			float length = sqrt( k1*k1 + k2*k2 + k3*k3);
			normals[x].n[0] = k1 / length;
			normals[x].n[1] = k2 / length;
			normals[x].n[2] = k3 / length;
			printf("%i %f %f %f \n", x, normals[x].n[0], normals[x].n[1], normals[x].n[2]);

			vertexnormals[temp1].v[0] += normals[x].n[0];
			vertexnormals[temp1].v[1] += normals[x].n[1];
			vertexnormals[temp1].v[2] += normals[x].n[2];

			vertexnormals[temp2].v[0] += normals[x].n[0];
			vertexnormals[temp2].v[1] += normals[x].n[1];
			vertexnormals[temp2].v[2] += normals[x].n[2];

			vertexnormals[temp3].v[0] += normals[x].n[0];
			vertexnormals[temp3].v[1] += normals[x].n[1];
			vertexnormals[temp3].v[2] += normals[x].n[2];
			printf("%i %f %f %f \n", x, vertexnormals[temp1].v[0], vertexnormals[temp1].v[1], vertexnormals[temp1].v[2]);
	}
}

void loadFile( char* filename ) 
{
	FILE* file;
	char line[255], comment[255]; 
	float x,y,z; 
	int a,b,c,d;
	int i=0;
	int j=0;

	file = fopen( filename, "r" );
	while ( fgets( line, sizeof(line), file ) != NULL ) 
	{
		if (sscanf( line, "#%s", comment ) ) 
		{
			printf( "valid: comment %s\n\n", comment );
		}

		if (sscanf( line, "v %f %f %f", &x, &y, &z ) )
		{
			vertex[i].v[0] = x;
			vertex[i].v[1] = y;
			vertex[i].v[2] = z;

			printf(" v[0] = %f \n", vertex[i].v[0]);
			printf(" v[1] = %f \n", vertex[i].v[1]);
			printf(" v[2] = %f \n\n", vertex[i].v[2]);
			
			i++;			
		}

		if (!strchr(line, '/'))
		{
			if (sscanf( line, "f %i %i %i %i", &a, &b, &c, &d) ) 
			{
				faces[j].f[0] = a;
				faces[j].f[1] = b;
				faces[j].f[2] = c;

				printf(" f[0] = %i \n", faces[j].f[0]);
				printf(" f[1] = %i \n", faces[j].f[1]);
				printf(" f[2] = %i \n\n", faces[j].f[2]);

				j++;
			}
		}
		else
		{
			if (sscanf( line, "f %i/%i/%i %i/%i/%i %i/%i/%i ", &a, &b, &c, &a, &b, &c, &a, &b, &c ) ) 
			{
				i=i;
			}
		}
	}
	calculateNormal(vertex, j);
}

void inListeSpeichern()
{
	loadFile("shuttle.obj");
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize (500, 500); 
	glutInitWindowPosition (100, 100);
	glutCreateWindow (argv[0]);
	inListeSpeichern();
	LoadGLTextures();
	init ();
	glutDisplayFunc(SchwarzStefanieDisplay); 
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glEnable(GL_DEPTH_TEST);
	glutIdleFunc(idle);
	glutMainLoop();
	return 0;
}
